#include "HandlerActualizacionPadre.h"
#include "Almacenamiento.h"
#include "BloqueBSharp.h"
#include "ControladorAlmacenamientoBSharp.h"
#include "Registro.h"

#include "DatoBSharp.h"

namespace Toolkit
{

HandlerActualizacionPadre::HandlerActualizacionPadre(
        ControladorAlmacenamientoBSharp *controlador, const TipoSerializacion &tSerializacion)
{
    this->actual = NULL;
    this->padre = NULL;
    this->siblingIzquierdo = NULL;
    this->siblingDerecho = NULL;

    this->controlador = controlador;
    this->tSerializacion = tSerializacion;
}

HandlerActualizacionPadre::~HandlerActualizacionPadre()
{
    if (this->actual != NULL)
    {
        delete this->actual;
        this->actual = NULL;
    }
    if (this->padre != NULL)
    {
        delete this->padre;
        this->padre = NULL;
    }
    if (this->siblingDerecho != NULL)
    {
        delete this->siblingDerecho;
        this->siblingDerecho = NULL;
    }
    if (this->siblingIzquierdo != NULL)
    {
        delete this->siblingIzquierdo;
        this->siblingIzquierdo = NULL;
    }

}

void HandlerActualizacionPadre::setBloqueActual(const BloqueBSharp *actual,
        const long int &posPadre, const BloqueBSharp *padre, const BloqueBSharp *siblingIzquierdo,
        const BloqueBSharp *siblingDerecho)
{
    if (this->actual != NULL)
    {
        delete this->actual;
        this->actual = NULL;
    }
    if (this->padre != NULL)
    {
        delete this->padre;
        this->padre = NULL;
    }
    if (this->siblingDerecho != NULL)
    {
        delete this->siblingDerecho;
        this->siblingDerecho = NULL;
    }
    if (this->siblingIzquierdo != NULL)
    {
        delete this->siblingIzquierdo;
        this->siblingIzquierdo = NULL;
    }

    this->actual = dynamic_cast<BloqueBSharp*> (actual->clonar());

    this->posPadre = posPadre;
    //El padre puede ser null??
    this->padre = dynamic_cast<BloqueBSharp*> (padre->clonar());

    if (siblingDerecho != NULL)
    {
        this->siblingDerecho
                = dynamic_cast<BloqueBSharp*> (siblingDerecho->clonar());
    }
    if (siblingIzquierdo != NULL)
    {
        this->siblingIzquierdo
                = dynamic_cast<BloqueBSharp*> (siblingIzquierdo->clonar());
    }

}

void HandlerActualizacionPadre::actualizar(Almacenamiento &almacenamiento,
        const BloqueBSharp *nuevoActual, const BloqueBSharp *nuevoIzquierdo,
        const BloqueBSharp *nuevoDerecho, WrapperComponente &wrapper)
{
    BloqueBSharp *nuevoPadre = dynamic_cast<BloqueBSharp*>(this->padre->clonar());

    //Hago las actualizaciones sobre el nuevoPadre
    if(this->siblingIzquierdo != NULL && nuevoIzquierdo != NULL) {
        reemplazarClaveEnPadre(this->siblingIzquierdo, nuevoIzquierdo, nuevoPadre);
    }

    reemplazarClaveEnPadre(this->actual, nuevoActual, nuevoPadre);

    if (this->siblingDerecho != NULL && nuevoDerecho != NULL) {
        reemplazarClaveEnPadre(this->siblingDerecho, nuevoDerecho, nuevoPadre);
    }

    propagarNuevoPadre(nuevoPadre, almacenamiento, wrapper);

    delete nuevoPadre;

}

void HandlerActualizacionPadre::reemplazarClaveEnPadre(const BloqueBSharp *bloqueViejo,
        const BloqueBSharp *bloqueNuevo, BloqueBSharp *padre)
{
    Clave *maxima = bloqueViejo->claveMaxima();
    Clave *nuevaMaxima = bloqueNuevo->claveMaxima();

    if(maxima != nuevaMaxima) {
        Registro *registro = dynamic_cast<Registro*>(padre->remover(*maxima));
        DatoBSharp *datoViejo = dynamic_cast<DatoBSharp*>(registro->clonarDato());

        DatoBSharp datoNuevo(*nuevaMaxima, datoViejo->getPosicion(), tSerializacion);
        registro->setDato(datoNuevo);
        padre->agregar(registro, *nuevaMaxima);

        delete datoViejo;
    }

    delete maxima;
    delete nuevaMaxima;
}

void HandlerActualizacionPadre::propagarNuevoPadre(BloqueBSharp *nuevoPadre, Almacenamiento &almacenamiento,
        WrapperComponente &wrapper)
{
    //comienzo la propagacion, ahora con el padre como el nodo actual
    if (!controlador->esRaiz(*(this->padre))) {
        //primero seteo las variables del handler
        long int posAbuelo = controlador->buscarPadre(padre);

        BloqueBSharp *abuelo = controlador->administradorBuffering.buscarBloque(posAbuelo);
        Clave *claveMaximaPadre = padre->claveMaxima();

        long int posIzq = abuelo->siblingIzquierdo(*claveMaximaPadre);
        BloqueBSharp *siblingIzquierdo = NULL;
        if (posIzq != -1) {
            siblingIzquierdo = controlador->administradorBuffering.buscarBloque(posIzq);
        }

        long int posDer = abuelo->siblingDerecho(*claveMaximaPadre);
        BloqueBSharp *siblingDerecho = NULL;
        if (posDer != -1) {
            siblingDerecho = controlador->administradorBuffering.buscarBloque(posDer);
        }

        long int posActual = posPadre;
        BloqueBSharp *actualPadre = dynamic_cast<BloqueBSharp*>(padre->clonar());
        this->setBloqueActual(actualPadre, posAbuelo, abuelo, siblingIzquierdo, siblingDerecho); //esto reemplaza posPadre
        delete actualPadre;

        //ahora actualizo //TODO demasiado parecido al agregar de la estrategia
        if(nuevoPadre->estaOverflow()) {
            controlador->manejarOverflow(almacenamiento, *nuevoPadre, posActual, wrapper, siblingIzquierdo, posIzq, siblingDerecho, posDer);
        } else if (nuevoPadre->estaUnderflow()) {
            controlador->manejarUnderflow(almacenamiento, *nuevoPadre, posActual, wrapper, posAbuelo, abuelo, posIzq, siblingIzquierdo, posDer, siblingDerecho);
        } else { //no esta overflow ni underflow
            controlador->guardarBloqueEnPosicion(*nuevoPadre, almacenamiento, posActual, wrapper);
            this->actualizar(almacenamiento, nuevoPadre, NULL, NULL, wrapper);
        }

        delete abuelo;
        delete claveMaximaPadre;
        if(siblingIzquierdo != NULL) {
            delete siblingIzquierdo;
        }
        if(siblingDerecho != NULL) {
            delete siblingDerecho;
        }

    } else { //es raiz
        if (nuevoPadre->estaOverflowRaiz()) {
            controlador->manejarOverflowRaiz(almacenamiento, *nuevoPadre, posPadre, wrapper);
        } else {
            this->controlador->guardarBloqueEnPosicion(*nuevoPadre, almacenamiento, posPadre, wrapper);
        }
    }
}


void HandlerActualizacionPadre::actualizarPartirDosEnTres(Almacenamiento &almacenamiento,
        const BloqueBSharp &bloqueNuevo, const unsigned long int &posNuevo,
        const BloqueBSharp *nuevoActual, const BloqueBSharp *nuevoIzquierdo,
        const BloqueBSharp *nuevoDerecho, WrapperComponente &wrapper)
{
    BloqueBSharp *nuevoPadre = dynamic_cast<BloqueBSharp*>(this->padre->clonar());

    //Hago las actualizaciones sobre el nuevoPadre
    if(this->siblingIzquierdo != NULL && nuevoIzquierdo != NULL) {
        reemplazarClaveEnPadre(this->siblingIzquierdo, nuevoIzquierdo, nuevoPadre);
    }

    reemplazarClaveEnPadre(this->actual, nuevoActual, nuevoPadre);

    if (this->siblingDerecho != NULL && nuevoDerecho != NULL) {
        reemplazarClaveEnPadre(this->siblingDerecho, nuevoDerecho, nuevoPadre);
    }

    //agrego la clave del nuevo Bloque
    Clave *maximaNuevo = bloqueNuevo.claveMaxima();
    DatoBSharp dato(*maximaNuevo, posNuevo, tSerializacion);
    Registro *registro = new Registro(tSerializacion);
    registro->setDato(dato);
    nuevoPadre->agregar(registro, *maximaNuevo);

    propagarNuevoPadre(nuevoPadre, almacenamiento, wrapper);

    delete maximaNuevo;
    delete nuevoPadre;

}

void HandlerActualizacionPadre::actualizarPartirUnoEnDos(Almacenamiento &almacenamiento,
        const BloqueBSharp &bloqueNuevo, const unsigned long int &posNuevo,
        const BloqueBSharp *nuevoActual, WrapperComponente &wrapper)
{
    actualizarPartirDosEnTres(almacenamiento, bloqueNuevo, posNuevo, nuevoActual, NULL, NULL, wrapper);

}

void HandlerActualizacionPadre::actualizarUnirTresEnDos(Almacenamiento &almacenamiento,
        const BloqueBSharp *nuevoIzquierdo, WrapperComponente &wrapper)
{
    BloqueBSharp *nuevoPadre = dynamic_cast<BloqueBSharp*>(this->padre->clonar());

    //elimino la clave del nodo que se fue
    Clave *maximaEliminado = this->actual->claveMaxima();
    delete nuevoPadre->remover(*maximaEliminado);

    //actualizo la clave del nodo izquierdo
    if(this->siblingIzquierdo != NULL && nuevoIzquierdo != NULL) {
        reemplazarClaveEnPadre(this->siblingIzquierdo, nuevoIzquierdo, nuevoPadre);
    }

    propagarNuevoPadre(nuevoPadre, almacenamiento, wrapper);

    delete maximaEliminado;
    delete nuevoPadre;
}

}
